#include <dirent.h>
#include <stdio.h>
#include <rid.h>
#include <stdint.h>
#include <stdlib.h>

#define STYLE_9
#include <fn85.h>
#include <rid_fn85.h>

#include <unfy/unfy.h>
#include <unfy/unfp.h>
#include <rls/rls.h>
#include <rls/rlsp.h>

#define NIT_SHORT_NAMES
#include <nit/list.h>
#include <nit/set.h>
#include <nit/map.h>
#include <nit/err.h>
#include <nit/file.h>
#include <nit/buf.h>
#include <nit/gap.h>

#include "../utilize.h"
#include "../examine.h"
#include "../front.h"

#include "acts.h"

static Unfy_stat
act_add(Rls_mode mode, Rls_srch *srch, void *extra);

static Unfy_stat
act_gensym(Rls_mode mode, Rls_srch *srch, void *extra);

Rls_act ilk_acts[] = {
	[ILK_ACT_ADD] = act_add,
	[ILK_ACT_GENSYM] = act_gensym,
};

static const Rid act_ids[] = {
	/* add_________________ */
	[ILK_ACT_ADD] = { 0x1f, 0x98, 0x8a, 0xb8, 0xfb, 0xe0, 0x0e, 0x10,
			  0xfb, 0xe0, 0x0e, 0x10, 0xfb, 0xe0, 0x0e, 0x10 },
	/* gensym______________ */
	[ILK_ACT_GENSYM] = { 0x32, 0x4e, 0x0e, 0x93, 0x47, 0x69, 0xfd, 0xf6,
			     0xfb, 0xe0, 0x0e, 0x10, 0xfb, 0xe0, 0x0e, 0x10 },
};

static const char *act_heads[] = {
	[ILK_ACT_ADD] =
	"(const sys_________________ const add_________________"
	" var id__________________ var head________________"
	" var body________________)",
	[ILK_ACT_GENSYM] =
	"(const sys_________________ const gensym______________"
	" var sym_________________)",
};

/* id__________________ */

static const Rid id_rid = { 0x38, 0x84, 0x13, 0x2b, 0xfb, 0xe0, 0x0e, 0x10,
			    0xfb, 0xe0, 0x0e, 0x10, 0xfb, 0xe0, 0x0e, 0x10 };
/* head________________ */
static const Rid head_rid = { 0x35, 0x69, 0x1f, 0x92, 0xfb, 0xe0, 0x0e, 0x10,
			      0xfb, 0xe0, 0x0e, 0x10, 0xfb, 0xe0, 0x0e, 0x10 };

/* body________________ */
static const Rid body_rid = { 0x23, 0x1c, 0x14, 0x52, 0xfb, 0xe0, 0x0e, 0x10,
			      0xfb, 0xe0, 0x0e, 0x10, 0xfb, 0xe0, 0x0e, 0x10 };

/* sym_________________ */
static const Rid sym_rid = { 0x58, 0x5f, 0xa6, 0x9c, 0xfb, 0xe0, 0x0e, 0x10,
			     0xfb, 0xe0, 0x0e, 0x10, 0xfb, 0xe0, 0x0e, 0x10 };

/* Act functions */

static Unfy_stat
act_add(Rls_mode mode, Rls_srch *srch, void *extra)
{
	Ilk_sys *sys = extra;
	Unfy_term *id = unfy_bind_get(srch->rbind, id_rid);
	Unfy_term *head = unfy_bind_get(srch->rbind, head_rid);
	Unfy_term *body = unfy_bind_get(srch->rbind, body_rid);
	Ilk_grp *grp;
	Unfy_list *body_list;

	if (mode == RLS_SRCH) {
		if (id && body && id->type == UNFY_CONST &&
		    body->type == UNFY_LIST &&
		    !ilk_rule_valid(head, body->u.list))
			return UNFY_YES;

		return UNFY_NO;
	}

	if (!(grp = ilk_grp_get_term(sys, head, NULL)))
		return UNFY_ERR;

	if (!(head = unfy_term_copy(head, &sys->lib->rec.unfy))) {
		err_cust(&sys->lib->err, "Failed to copy rule head");
		goto err_term;
	}

	if (unfy_list_copy(&body_list, body->u.list, &sys->lib->rec.unfy) < 0) {
		err_cust(&sys->lib->err, "Failed to copy rule body");
		goto err_list;
	}

	if (!ilk_rule_add(sys, id->u.id, head, body_list, sys->srch_cnt))
		goto err_add;

	return UNFY_YES;
err_add:
	unfy_recycle_list(&sys->lib->rec.unfy, body_list);
err_list:
	unfy_recycle_term(&sys->lib->rec.unfy, head);
err_term:
	return UNFY_ERR;
}

static Unfy_stat
act_gensym(Rls_mode mode, Rls_srch *srch, void *extra)
{
	Ilk_sys *sys = extra;
	Unfy_term *var;

	if (mode == RLS_RUN)
		return UNFY_YES;

	if ((var = unfy_bind_get(srch->rbind, sym_rid))->type != UNFY_VAR)
		return UNFY_NO;

	/* there will only be one for the input */
	unfy_term_init(srch->fbind->term, UNFY_CONST, NULL);
	unfy_recycle_bind(&sys->lib->rec.unfy, srch->lbind);
	srch->lbind = NULL;
	unfy_recycle_bind(&sys->lib->rec.unfy, srch->rbind);
	srch->rbind = NULL;
	unfy_recycle_bind(&sys->lib->rec.unfy, srch->nbind);
	srch->nbind = NULL;
	return UNFY_YES;
}

Ilk_grp *
ilk_act_add2(Ilk_sys *sys, Ilk_act_num act_num)
{
	size_t pos = 0;
	const char *error;
	Unfy_term *head;
	Ilk_grp *grp;

	if (!(head = unfp_term_parse(act_heads[act_num], &pos,
				     &error, &sys->lib->rec.unfy))) {
		err_cust(&sys->lib->err, error);
		return NULL;
	}

	if (!(grp = ilk_act_add(sys, act_ids[act_num], head, act_num)))
		unfy_recycle_term(&sys->lib->rec.unfy, head);

	return grp;
}
